Currently, one of the most important methodologies in studying differentiated products industries involves estimating the demand and variable cost curves for and industry using the methods developed in [@Berry1994] and [@BLP1995]. The goal is usually to take these curves and simulate counter-factuals e.g. prices after a merger. This package, SimNashPrice, is designed to assist with this simulation process.

The path from the data to the counter-factual has three steps. First, data on market shares, prices and product characteristics are used to estimate the demand curve. Second, assuming Firm's choose prices in order to maximize profits the demand estimates can be used to recover the variable costs. Finally, the researcher can use the demand and variable costs to simulate a wide array of counter-factuals. The key step in this simulation is solving for new prices. This is not a simple optimization problem since firm's are modeled as choosing prices in a game, and playing a Nash equilibrium (N.E.). [@MorrowSkerlos] have developed a reliable algorithm for solving for these prices, and I have implanted their algorithm in this package to make it available to other researchers.

While the package focuses on computing N.E. prices, it can also be used to recover variable profits/markups/costs from demand estimates and data. Further simulating N.E. prices is not just useful for counter-factuals: it is also important for generating artificial data sets that can be used to test proposed estimation routines and software.

I'll start out this vignette by laying out the mathematical model of consumer and firm behavior that are being used, then I'll explain the algorithm for computing N.E. prices given by [@MorrowSkerlos]. Finally, I will go through some examples of how to put the package to use. I will generate a artificial data set for testing estimation routines, and show a counter-factual simulation of a merger.

Modeling Framework: demand and variable cost

The model starts with a specification for consumer utility: $$ u_{ijt} = (\alpha + \alpha_i)p_{jt} + \delta_{jt} + \xi_{jt} + \mu_{ijt} + \epsilon_{ijt} $$ Consumer $i$ gets utility $u_{ijt}$ from consumer product $j$ in market-time observation $t$. His utility is a function of the price $p_{jt}$, and consumer $i$ receive dis-utility $\alpha+\alpha_i$ from the price. The consumer also gets a fixed payoff from consuming the good $\delta_{jt}$ that is shared across consumers and a function of product and market-time (henceforth period) specific variables observed by the econometric, and there is another fixed payoff that is not observable by the econometrician given by $\xi_{jt}$ which will be referred to as the structural error of demand shock. Finally there are two payoffs that are idiosyncratic to the consumer. First, is a payoff for consuming product $j$ in period $t$ that is a function of observable variables, but is consumer specific. Second, is an idiosyncratic payoff that is unobserved and henceforth will be assumed to follow a type-1 extreme value distribution and be i.i.d. across products and periods.

Because of the type-1 extreme value distribution the probability of consumer $i$ purchasing good $j$ in period $t$ as a function of the price will be:

$$s_{ijt} = \frac{e^{(\alpha + \alpha_i)p_{jt} + \delta_{jt} + \xi_{jt} + \mu_{ijt}}}{\sum_{k=0}^{J_{t}}e^{(\alpha + \alpha_i)p_{kt} + \delta_{kt} + \xi_{kt} + \mu_{ikt}}} $$

The researcher usually considers one products to be the 'outside option' in other words the consumer may choose not to spend money on the good in question and instead purchase other types of goods. Label this product $0$ and let its utility be $u_{0}$. For estimation purposes this must be normalized with $u_{0}=0$, and I will follow that convention--but the reader should note that for the purposes of simulating a theoretical situation they could take any normalization. (The default behavior of SimNashPrice is to take the $0$ normalization). Under that normalization the expression above simplifies to: $$s_{ijt} = \frac{e^{(\alpha + \alpha_i)p_{jt} + \delta_{jt} + \xi_{jt} + \mu_{ijt}}}{ 1+\sum_{k=1}^{J_{t}}e^{(\alpha + \alpha_i)p_{kt} + \delta_{kt} + \xi_{kt} + \mu_{ikt}}} $$ The above expression gives the individual specific choice probabilities in the random coefficients logit model. This model allows for a great deal of heterogeneity across consumers via $\alpha_{i}$ and $\mu_{ijt}$. This flexibility allows it to realistically model substitution patterns. If $\alpha_{i} = 0$ and $\mu_{ijt} = 0$ then the model is referred to as the simple logit model. SimNashPrice allows the researcher to use either specification, but in this vignette I will describe only the more realistic random coefficients specification.

I will use the following convention for denoting vectors: the vector of all of the consumer's choice probabilities is $s_{jt}$ omitting the index of the variable that indexes the new vector. Therefore I will write $F(\mu_{it})$ to denote the distribution of across consumers of idiosyncratic utility from all of the $J_{t}$ products available in period $t$. Also let $F(\alpha_i)$ be the distribution of $\alpha_{i}$ in the population. Using these definitions we can integrate over the population of consumers to get the expected market share of each product in a given period: $$s_{jt} = \int \int s_{ijt}\, dF(\mu_t)\,dF(\alpha_i)$$ There is much to say about estimation of this type of model see [@BLP1995, @Berry1994, @Nevo2001], but the emphasis here is just on simulating it and with a specification for the distributions in the above expression it is easy to use a simulation procedure to approximate $s_{jt}$.

$s_{jt}$ is referred to as the market share of product $j$, and written as a function of the prices of all of the products $s_{jt}(p_t)$ can be thought of as a demand curve. Firm's facing this demand curve choose prices to maximize profits, but because profits depend on the choices of other firms must choose prices subject to the constraint that other firm's prices are also maximizing profits conditional on their own choice. To make this formal index firms by $f$ and denote the prices and shares of firm $f$ as the vectors $p_{t}^{f}$ and $s_{t}^{f}$ and the prices and shares of all other firms as $p_{t}^{-f}$ and $s_{t}^{-f}$. Let $p_{t}^{-f}(p_{t}^{f})$ denote the other firm's best response (profit maximizing response) to firm $f$'s prices.

The firm faces the following profit maximization problem in N.E.:

$$ max_{p_{t}^{f}}\ s_{t}^{f}(p_{t}^{f},\, p_{t}^{-f})^{T}(p_{t}^{f}-c_{t}^{f})M_t \ s.t.\ p_{t}^{-f} = p_{t}^{-f}(p_{t}^{f})$$ With $c_{t}^{f}$ being firm $f$ per unit cost of producing and selling products, and $M_t$ is the size of the market: the number of possible purchases in the market in period $t$. The market share is then the percentage of the possible sales that go to each product.

SimNashPrice assumes the marginal costs are of the following form: $$ c_{tj} = exp(\nu_{jt} + \omega_{jt}) $$ Cost like utility is decomposed into $\nu_{jt}$, a part that depends on observable variables, and $\omega_{jt}$ a part that is unobservable to the econometrician.

The necessary condition for profit maximization, referred to in economics as the first order condition (f.o.c.) is: $$ s_{t}^{f}(p_{t}^{f},\, p_{t}^{-f})^T + D_{p_{t}^{f}} s_{t}^{f}(p_{t}^{f}-c_{t}^{f}) = 0 $$ $D_{p_{t}^{f}} s_{t}^{f}$ is the jacobian of the firms market shares with respect to its own prices. This is a dense matrix with entries: $(D_{p_{t}^{f}} s_{t}^{f}){nh} = \frac{\partial s{ht}^{f} }{ \partial p_{ht}^{f} }$. The constraint in the profit maximization problem requires that this condition hold simultaneously for all of the firms. Thus we can write a system of necessary conditions (stacking all firms on top of each other): $$ s_{t}(p_{t})^T + D_{p_{t}} s_{t} (p_{t}-c_{t}) = 0$$ The matrix $D_{p_{t}} s_{t}$ is referred to by [@MorrowSkerlos] as the 'intra-firm' jacobian of market shares. This matrix has entries: $$ (D_{p_{t}} s_{t}){nh} = \frac{\partial s{nt}^{f} }{ \partial p_{ht}^{f} } 1[product\ p\ and\ q\ owned\ by\ the\ same\ firm] $$ Another way to define this matrix whenever it is formed from stacking the firm's f.o.c.'s as $D_{p_{t}^{f}} s_{t}^{f} \otimes I_{F}$, where $I_F$ is the $F$x$F$ identity matrix and $F$ is the number of firms. The system of first order conditions are necessary conditions for firms to be choosing prices that form a N.E..

The primary goal of this package is to provide an easy to use tool for solving this system of f.o.c.'s, because this is a necessary step for any counter-factual simulation or other type of simulation. Solving this non-linear fixed point problem is non-trivial. [@MorrowSkerlos] show that many common/naive approaches will fail when the search cannot be started close to the solution. In the next section I will explain their novel approach to solving this problem.

Fixed Point Computation of N.E. Prices

Consider again the f.o.c.'s: $$ s_{t}(p_{t})^T + D_{p_{t}} s_{t} (p_{t}-c_{t}) = 0$$

[@MorrowSkerlos] Proposition 2.3 applies the Leibniz rule to show that the intra-firm jacobian can be written as the difference between two matrices. Applying their result to this model set up gives:

$$ D_{p_{t}} s_{t} = \Lambda (s_{t}) - \Gamma (s_{t}) $$

Where $\Lambda (s_{t})$ and $\Gamma (s_{t})$ are matrices, and $\Lambda (s_{t})$ is diagonal. These matrices have entries: $$(\Lambda (s_{t})){n} = \int \int -(\alpha + \alpha_i) s{iht}\,dF(\mu_t)\,dF(\alpha_i) $$ $$(\Gamma (s_{t})){nh} = \int \int s{int} s_{iht} -(\alpha + \alpha_i)\,dF(\mu_t)\,dF(\alpha_i)\ 1[product\ p\ and\ q\ owned\ by\ the\ same\ firm] $$ Plug these matrices into the f.o.c.: $$s_{t}(p_{t})^T + D_{p_{t}} s_{t} (p_{t}-c_{t}) = 0$$ $$s_{t}(p_{t})^T + (\Lambda (s_{t}) - \Gamma (s_{t})) (p_{t}-c_{t}) = 0$$ $$p_{t} = \Lambda (s_{t})^{-1}( \Gamma (s_{t}) (p_{t}-c_{t}) - s_{t}(p_{t})^T) + c_{t}$$ The last equation gives a fixed point problem for prices. [@MorrowSkerlos] call this fixed point the 'zeta fixed point', and define: $$\zeta(p_t) = \Lambda (s_{t})^{-1}( \Gamma (s_{t}) (p_{t}-c_{t}) - s_{t}(p_{t})^T) $$

Other approaches in the literature to solving for prices might be to solve the system of f.o.c.'s directly using a root-solver (gradient search) or by iterating on the f.o.c. directly (a fixed point using the matrix $D_{p_{t}} s_{t}$. [@MorrowSkerlos] show in simulations that these methods are not reliable when the researcher does not have a good a priori guess for equilibrium prices. My own limited experience agrees. In particular my research involves changing the set of available products. The usual guess for computing counter-factual prices are the observed prices, but with new products there are no observed prices for those products in that market observation. Iterating on the zeta fixed point requires no numerical optimization nor linear algebra. The matrix $\Lambda(s_t)$ is diagonal so its inverse can be found by straightforward arithmetic. In practice the zeta fixed point is fast because of this simplicity. It is important to point out that this fixed point is only available for mixed logit models (random coefficients logit, simple logit, and nested logit).

In the next section I will show an example of how SimNashPrice implements the zeta fixed point, and I will show several other features of the package that hopefully make it helpful for researchers.

Artifical Data Set: tidy demand data

I divide this section into two parts: first, I will set up a single market and show how the class that models the markets are structured and how the basic computations work; second, I show how to loop over the process of market creation to generate an artificial data set and then tidy that data set into something that could be used for estimation.

Creating the first market

The first step will be to install and load the package:

#devtools::install_github('joearossetti/SimNashPrice') # only need to install once
library(SimNashPrice)
set.seed(1234)

The next step is to set up some parameters for the market since I am simulating a market from scratch. Using the package with data and estimated demand curves does not require all of this set-up--the interested reader should skip to the final section on simulating a merger to see the package used in this way.

In this example products will have two observable characteristics upon which $\delta_{jt}$, $\mu_{ijt}$ and $\nu_{jt}$ will depend. beta will determine how the consumer's utility depends on the product characteristics, and gamma will determine how the variable cost will depend on the product characteristics.

alpha <- 5 # fixed part of price coeff (must be positive)
beta <- c(-3, -1) # consumer utility from product chars
gamma <- c(1, 0.5)/10 # variable cost from product chars

Each product characteristic will have two levels. There will be 4 potential products--every combination of two binary characteristics

## Make a list of characteristics
char_levels <- list(char1 = c(0,1), char2 = c(0,1))

## make a matrix of all potential products
XP <- expand.grid(char_levels)
J <- dim(XP)[1] # number of potential products
XP <- data.frame(XP, prodname=1:J)
print(XP)

I organize this information into a table with each product observable component of utility and variable cost (mc) as follows:

my_prod_table <- data.frame(j = XP$prodname,
                            delta = (cbind(XP$char1, XP$char2) %*% beta),
                            mc = (cbind(XP$char1, XP$char2) %*% gamma))
print(my_prod_table)

I randomly pick some products for the firms to offer by generating 2 J vectors. Each element is 1 if that product is offered by that firm, and 0 otherwise.

a1 <- c(0,1,1,1)
a2 <- c(1,1,0,0)
print(a1)
print(a2)

So firm 1 offers products r which(a1==1) and firm 2 offers products r which(a2==1). I construct a new data frame that contains each of the products on the market and the identity of the firm offering it.

my_mkt_prods <- rbind(my_prod_table[which(a1==1),], my_prod_table[which(a2==1),])
my_mkt_prods <- data.frame(my_mkt_prods, firm = c(rep(1,sum(a1==1)),  rep(0,sum(a2==1))))
print(my_mkt_prods)

Next I generate the structural errors and random coefficients.

First, I draw $\xi_jt$ and $\omega_jt$ from standard normal distributions (there are 5 products on the market so I draw samples of 5).

Xi <- rnorm(5, 0, 1)
Omega <- rnorm(5, 0, 1)/100

SimNashPrice handles the random coefficients by taking as inputs matrices of draws from the distributions $F(\mu_{it})$ and $F(\alpha_i)$. This allows the researcher to choose any specification of distributions and draw from it using any simulation method. It also makes most of the computations within SimNashPrice reduce to linear algebra and avoids random number generation within the computations.

I will assume that $\alpha_{i}$ is i.i.d. $N(0,1)$. Let $B$ denote the number of simulation draws the researcher wants to use. SimNashPrice needs a $B$x$1$ matrix of draws from the distribution of $\alpha_{i}$.

my_alphais <- t(rnorm(500, 0, 1))
dim(my_alphais)
class(my_alphais)

$\mu_{ijt}$ depends on the product characteristics. Let $x_{1j}$ be the indicator of whether product $j$ has characteristic $1$. I paramatrize $\mu_{ijt}$ as: $$ \beta_{1i} x_{1j} + \beta_{2i} x_{2j} $$ I draw $\beta_{i1}$ and $\beta_{i2}$ from $N(0,1)$:

beta_is <- cbind(rnorm(500, 0, 1), rnorm(500, 0, 1))

Then I compute the $\mu_{it}$ vectors using matrix multiplication:

my_muis <- as.matrix(XP[,1:2]) %*% t(beta_is)
dim(my_muis)
print(my_muis[1:4,1:4])

Each column is a vector $\mu_{it}$, and each row corresponds to a product. Since not every product is offered I select the rows that correspond to the rows of my_mkt_prods:

my_mkt_muis <- my_muis[my_mkt_prods$j,]
print(my_mkt_muis[1:5,1:4])

With all of these elements ready the constructor for the 'rldmkt' class can be called:

my_rldmkt_obj <- rLogit_Demand_Market$new(
    Firms = my_mkt_prods$firm, #vector of firm identities
    Muij_mat = my_mkt_muis, # matrix of mus
    Aij_mat = my_alphais, # matrix of alphas
    Prod_ids = my_mkt_prods$j, # vector of product ids
    Delta = my_mkt_prods$delta, # vector of product mean utilities
    Mc_fixed = my_mkt_prods$mc, # vector of marginal costs
    Mc_error = Omega, # mc errors
    Struct_error = Xi, # utility errors
    U_out_opt = 0, # utility of outside option
    Alpha = alpha # alpha (fixed part of price coef)
  ) 

rldmkt is a R6 class [@R6] and abbreviation stands for "random coefficient logit demand market". There is also a "logit demand market" class called ldmkt. The R6 class system allows pass by reference, and is higher performance than R's built in class system with reference semantics: RC. Pass by reference is important for performance when doing object oriented programming in R. R by default copies on modification, which means the standard objected oriented methodology in R (as in S3 or S4) of passing objects to generic functions and returning the object with a modification (or in a new form) generally ends up creating lots of copies of the object during evaluation. In this case changing from S3 to R6 resulted in about a 4 times speed up of my code.

There are several get methods provided with the form getFoo. The main way that SimNashPrice represents a market is through the private Market field. This data attribute is a data.frame similar to my_mkt_prods, lets take a look:

my_rldmkt_obj$getMarket()

With each row representing a product this data.frame holds all of the product specific information in an easy to read format. Notice that Price, Share, and Markup all default to 0. Actually these could have been set to anything using the constructor. When the methods that compute these quantities are called these fields will be filled in with shares, prices, and markups computed based on our specification of the primitives: utility and variable cost. It is equally possible to use the package to compute variable costs and markups from observed prices and shares by 'inverting' the firms' f.o.c's. I will cover this second process in the example that covers simulating counter-factuals.

I think of these two possible uses of the package as mappings: $$ primitives \rightarrow zeta\ fixed\ point\ \rightarrow prices,\ shares,\ markups$$ $$ prices,\ shares \rightarrow firm's\ f.o.c. \rightarrow variable\ cost$$

The object oriented programming paradigm represents these mappings as attributes of the model object: objects are collections of data (like prices) and functions (called methods) like the zeta fixed point that act on that data. I think this method of representing economic models provides a useful abstraction for computing.

Before using the zeta fixed point to fill in the missing prices and shares, lets take a quick look at some of the other data fields of the 'rldmkt' object.

The sums $(\alpha + \alpha_i)$ for each draw:

my_rldmkt_obj$getDerivPrice()[1:5]

The fixed part of utility $(\delta_{jt} + \xi_{jt})$:

my_rldmkt_obj$getUjs()[1:5]

Total utility minus the part that is a function of prices $(\delta_{jt} + \xi_{jt} + \mu_{ijt})$ for each draw:

my_rldmkt_obj$getUijs()[1:5,1:5]

The variable cost $\nu_{jt}+\omega_{jt}$:

my_rldmkt_obj$getCjs()[1:5]

Given the model primitives the shares and prices in equilibrium can be computed. The mapping the does this is the zeta fixed point:

$$\zeta(p_t) = \Lambda (s_{t})^{-1}( \Gamma (s_{t}) (p_{t}-c_{t}) - s_{t}(p_{t})^T) $$ In order to evaluate $\zeta(\cdot)$ it will be necessary to compute shares and the matrices $\Lambda(\cdot)$ and $\Gamma(\cdot)$. The 'rldmkt' class has a method share that computes market shares:

rldmkt_share <- function(){
  Index <- private$uijs - private$Market[['Price']] %*% private$Deriv_price
  S <- exp(Index)
  Denom <- exp(private$U_out_opt) + colSums(S)
  private$Si <- S / Denom # key computation re-used many times
  private$Market[['Share']] <- rowMeans(private$Si)
  invisible(self)
}

I want to highlight the private field Si that gets computed by this method. These are the $s_{ijt}$. They are collected in a matrix with rows for products and columns for individuals. These shares also enter into the computation of the intra-firm jacobian. By computing these and storing them in a private field all other methods in the function can re-use these values without computing them. In particular the method that computes the matrices that form the intra-firm jacobian will re-use these values:

rldmkt_Ds_fun <- function(){
  Lambda_p <- -diag(rowMeans(private$Si*as.numeric(private$Deriv_price)))
  Gamma_p <- gamma_helper(Sr = private$Si, ar=private$Deriv_price, Or=private$O)
  D_p <- Lambda_p + Gamma_p
  Ds <- list(Lambda_p, Gamma_p, D_p)
  names(Ds) <- c("Lambda_p", "Gamma_p", "D_p")
  class(Ds) <- "LogitShareJacobian"
  private$Ds <- Ds
  invisible(self)
}

This method calls the function gamma_helper which is a call to a c++ function written using Rcpp [@rcpp] and RcppArmadillo. Computing $\Gamma(\cdot)$ requires computing the matrix (something like a substitution matrix) for each individual (columns of Si) and then taking the mean of corresponding entries in the list of matrices. Doing this in R would be possible, but c++ loops are fast and RcppArmadillo [@rcpparmadillo] makes linear algebra in c++ as easy as it is in R. gamma_helper is displayed below:

#include <RcppArmadillo.h>
using namespace Rcpp;
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
NumericMatrix gamma_helper(NumericMatrix Sr, NumericVector ar, NumericMatrix Or) {

  arma::mat S = Rcpp::as<arma::mat>(Sr);
  arma::vec a = Rcpp::as<arma::vec>(ar);
  arma::mat O = Rcpp::as<arma::mat>(Or);
  int R = a.n_rows;
  int J = S.n_rows;

  arma::mat G = arma::zeros<arma::mat>(J,J);

  for(int i = 0; i<R; i++) {
    G = G + ( (a[i] * ( S.col(i) * S.col(i).t() )) % O);
  }

  G = G * (1/ ((float) R));

  return Rcpp::wrap(G);
}

Notice that I compute Ds as Lambda_p + Gamma_p which differs from the definition of the intra firm jacobian: $D_{p_{t}} s_{t} = \Lambda (s_{t}) - \Gamma (s_{t})$. This is because I model $\alpha+\alpha_i$ as positive so that price enters as a negative in the utility function. Therefore there is a leading negative sign in the definition of each matrix, therefore I compute $\Lambda(\cdot)$ as Lambda_p <- -diag(foo) and the negative in front of $\Gamma(\cdot)$ cancels in the formula for $D_{p_{t}} s_{t}$.

All of the methods are exported as R function with the predicate classname_ e.g. rldmkt_. This appears to be the best way to document R6 objects. So if we want to look up the method "foo" in help you search "rldmkt_foo", and if you want to call it you leave off the class name predicate: my_obj$foo(). Let's call the share and intra-firm Jacobian methods and look at their output:

my_rldmkt_obj$share()
print(my_rldmkt_obj$getMarket())

my_rldmkt_obj$Ds_fun()
print(my_rldmkt_obj$getDs())

The loop continues until the first order conditions are within the tol of 0, then the loop will stop with the break command. Note that the method calls the share and Ds_fun methods itself--there is no need to call these first, and since these methods have access to the private data fields by reference the prices, shares and intra-firm jacobian are updated each time these methods are called and will have the value of the last iteration when the fixed point method terminates. Let's take a look at our model:

my_rldmkt_obj$zeta_fixed_point(tol=1e-6, max_iter = 1000)

print(my_rldmkt_obj$getMarket()) # markup is still 0 because we have not computed it

Generating a list of markets

Now that I have computed prices and shares for one market I want to generate a sample of markets with different products. I will define a factory function that contains all of the parameter definitions I want my market simulating function to use. This function will return a function market_maker that will act as a closure--capturing the parameters that were defined in the environment in which it was created. As coded below my factory function does not take any arguments, but if, for example, I wanted to generate samples with several different values of alpha I could make it an argument instead of a definition in the function body. These two functions wrap the code that was used above to generate the first market, but now the sets of products offered by each firm is random.

source('MarketMaker.R')

my_market_maker <- market_maker_factory() 

Now I will call the my_market_maker 1000 times. I store the results as a list column in a tibble (data.frame extension from the tidy-verse).

T_ <- 1000
library(tidyverse, quietly = TRUE)
Markets <- tibble(period = 1:T_,
                  marginal_cost_shifter = rnorm(T_))

Markets$market_obj = map(Markets$marginal_cost_shifter, .f=~my_market_maker(mc_shift = .x))

Storing objects in this way allows me to use dplyr/purrr to work with the objects. For example I want my data set to include shares and prices so I call the zeta_fixed_point method using purrr::map:

# wrap call to the zeta fixed point so errors are trapped
call_safely <- function(x){
  out <- tryCatch(x$zeta_fixed_point(tol = 1e-06, max_iter = 200), error = function(e){e})
}

Markets$markets_solved <- map(Markets$market_obj, .f=call_safely)

# check if any of the markets threw an error
which(map_lgl(Markets$markets_solved, .f=~class(.x)[1]!='rldmkt'))

The tibble print method thinks that the rldmkt objects are S3 objects, but they are actually R6 objects. This does not alter the underlying data or our ability to compute on it.

Now that we have 1000 observations of different markets lets extract the information from the rldmkt, and put them in a convenient form for estimating demand.

Tidying up

Tidy data has observations in rows, and variables in columns [@tidydata]. This paradigm has become a key design principle in R, and I highly recommended that data for demand estimation be stored in this way. In fact by storing the 1000 markets in the data.frame with a list column I have already made the data tidy, and making it look more like a traditional data set will be straightforward using the tools available in the tidyverse for working with (and creating) tidy data.

demand_data <- Markets %>% mutate(
    market_dfs = map(market_obj, .f=~.x$getMarket()%>%select(Prod_ids, Firms, Price, Share))
  ) %>% 
  select(period, marginal_cost_shifter, market_dfs) %>%
  unnest() %>%
  left_join(XP, by=c("Prod_ids" = "prodname"))

print(demand_data)
save(demand_data, file='demand_data.RData')

Simulating a Merger

References



joearossetti/SimNashPrice documentation built on May 19, 2019, 2:58 p.m.